package com.chezhujie.www.utils;

import net.coobird.thumbnailator.Thumbnails;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;

/**
 * 程序功能：把一个目录下所有png文件中的透明色，替换为指定颜色：洋红(0xFF00FF)
 * PNG中两个表涉及到这个改动，一个是PLTE，一个是tRNS(注意大小写）
 * PNG的格式为，除了前8个字节的头信息外，有若干个表构成
 * 每个表的格式为
 * 表数据长度4字节 （假设其值为n）
 * 表名 4字节
 * 表数据 n字节
 * CRC 4字节
 *
 * 其中PLTE表为调色板
 * 表数据为若干个由rgb组成的颜色值，每个颜色值3个字节
 *
 * tRNS表为透明色表
 * 表数据对应颜色表中的数据，每个字节对应一个颜色数据
 * 如果数据为0xFF表明不是透明色
 * 如果为0x00表明是透明色
 *
 * 本程序先搜索nTNS找到透明色的索引，并把透明色改为不透明
 * 然后把PLTE对应的颜色值改为指定颜色值
 * @author dabao
 *
 */
public class PngConverter {
    static byte[] buf = new byte[100000];
    public static int getInt(int nOffset, byte[] abBuffer) {
        return (abBuffer[0 + nOffset] & 0xff) << 24 | (abBuffer[1 + nOffset] & 0xff) << 16 | (abBuffer[2 + nOffset] & 0xff) << 8 | (abBuffer[3 + nOffset] & 0xff);
    }

    /**
     * 找到一个表的偏移
     * @param imgData
     * @param blockName
     * @return
     */
    private static int getTableOffset(byte[] imgData, String blockName) {
        int offset = 8;
        int blockLen = 0;
        while(offset < imgData.length) {
            blockLen = getInt(offset, imgData);
            if(imgData[offset + 4] == blockName.charAt(0) &&
                    imgData[offset + 5] == blockName.charAt(1) &&
                    imgData[offset + 6] == blockName.charAt(2) &&
                    imgData[offset + 7] == blockName.charAt(3)
                    ) {
                //找到了
                break;
            }
            offset += 4 + 4 + blockLen + 4;
        }
        return offset;
    }

    /**
     * 修改png文件
     * @param dis
     * @param r
     * @param g
     * @param b
     * @return 修改后的png文件，以字节数组的形态存在
     * @throws IOException
     */
    private static byte[] convertPng(InputStream dis, byte r, byte g, byte b) throws IOException {
        int imgLen = dis.read(buf);
        byte[] imgData = new byte[imgLen];

        System.arraycopy(buf, 0, imgData, 0, imgLen);

        //首先得到透明色的位置
        int offset = getTableOffset(imgData, "tRNS");
        if(offset >= imgData.length) {
            return null;
        }

        int blockLen = getInt(offset, imgData);
        int trnsoffset = offset + 4 + 4;

        int clrCnt = blockLen;
        //System.out.println("颜色数:" + clrCnt);
        int transIdx = -1;
        //System.out.println(imgData[offset] + ", " + imgData[offset + 1] + ", " + imgData[offset + 2]);
        for(int iclr = 0; iclr < clrCnt; iclr++) {
            if(imgData[trnsoffset + iclr] == (byte)0x00) {
                //在我的图片中，透明色只有一个
                //是透明色
                transIdx = iclr;
                imgData[trnsoffset + iclr] = (byte)0xFF;
                break;
            }
        }

        if(transIdx < 0) {
            return null;
        }

        //更新trans表的crc
        byte[] trnsData = new byte[clrCnt];
        System.arraycopy(imgData, trnsoffset, trnsData, 0, clrCnt);
        long crcValue = crc(trnsData, trnsData.length);
        int crc_idx = offset + 4 + 4 + blockLen;

        imgData[crc_idx] = (byte) ((crcValue & 0xFF000000) >> 24);
        imgData[crc_idx + 1] = (byte) ((crcValue & 0x00FF0000) >> 16);
        imgData[crc_idx + 2] = (byte) ((crcValue & 0x0000FF00) >> 8);
        imgData[crc_idx + 3] = (byte) (crcValue & 0x000000FF);

        //找到PLTE的位置
        offset = getTableOffset(imgData, "PLTE");
        if(offset >= imgData.length) {
            return null;
        }
        blockLen = getInt(offset, imgData);
        int clrOffset = offset + 4 + 4 + transIdx * 3;
        imgData[clrOffset] = r;
        imgData[clrOffset + 1] = g;
        imgData[clrOffset + 2] = b;

        byte[] crcData = new byte[4 + blockLen];
        System.arraycopy(imgData, offset + 4, crcData, 0, 4 + blockLen);
        crcValue = crc(crcData, crcData.length);
        crc_idx = offset + 4 + 4 + blockLen;

        imgData[crc_idx] = (byte) ((crcValue & 0xFF000000) >> 24);
        imgData[crc_idx + 1] = (byte) ((crcValue & 0x00FF0000) >> 16);
        imgData[crc_idx + 2] = (byte) ((crcValue & 0x0000FF00) >> 8);
        imgData[crc_idx + 3] = (byte) (crcValue & 0x000000FF);

        return imgData;
    }

    private static long[] crc_table = new long[256];
    private static int crc_table_computed = 0;
    /*
     * 对PNG图片的调测板部分数据进行CRC校验
     * 返回一个校验结束的long型变量
     */
    private static long crc(byte[] buf, int len) {
        int n;
        int k;
        long c;
        if (crc_table_computed == 0) {
            for (n = 0; n < 256; n++) {
                c = (long) n;
                for (k = 0; k < 8; k++) {
                    if ((int) (c & 1) != 0) {
                        c = 0xedb88320L ^ (c >> 1);
                    } else {
                        c = c >> 1;
                    }
                }
                crc_table[n] = c;
            }
            crc_table_computed = 1;
        }
        c = 0xffffffffL;
        for (n = 0; n < len; n++) {
            c = (crc_table[(int) (c ^ buf[n]) & 0xff]) ^ (c >> 8);
        }
        return c ^ 0xffffffffL;
    }

    /**
     * 递归遍历整个目录
     * @param dir
     * @throws IOException
     */
    public static void convert(File dir) throws IOException {
        File[] files = dir.listFiles();

        for(int ifile = 0; ifile < files.length; ifile++) {
            if(files[ifile].isDirectory()) {
                convert(files[ifile]);
                continue;
            }

            String fileName = files[ifile].getAbsolutePath();

            if(!".png".equals(fileName.substring(fileName.length() - 4))) {
                continue;
            }

            System.out.println("转换:" + files[ifile].getAbsolutePath());
            FileInputStream fileIn = new FileInputStream(files[ifile]);
            byte[] pngBytes = convertPng(fileIn, (byte)0xff, (byte)0, (byte)0xff);
            fileIn.close();
            if(pngBytes == null) {
                continue;
            }

            FileOutputStream fileOut = new FileOutputStream(files[ifile]);

            fileOut.write(pngBytes, 0, pngBytes.length);
            fileOut.close();
        }
    }


    
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
//		convert(new File("D:/Intelij_Idea/shineease/chezhujie/target/chezhujie/upload/comments/pic/"));
        //覆盖图
        File coverFile = new File("C:\\Users\\Administrator\\Desktop\\车主节\\图片测试\\透明测试.png");
//        输出文件
        File outputfile = new File("C:\\Users\\Administrator\\Desktop\\车主节\\图片测试\\222.png");
//        背景图片
//        File whiteFile = new File("C:\\Users\\Administrator\\Desktop\\车主节\\图片测试\\white.png");

        BufferedImage coverImg = ImageIO.read(coverFile);

        BufferedImage background = removeTransparent(coverImg);

        Thumbnails.of(background).scale(1f).outputFormat("jpg")
                .toFile(outputfile);//中等图


    }


    /**
     * 移除透明
     * @param coverImg
     * @return
     */
    public static BufferedImage removeTransparent(BufferedImage coverImg) {
        BufferedImage background = setWhite(coverImg.getWidth(),coverImg.getHeight());

        Graphics2D g2d = background.createGraphics();
        g2d.drawImage(coverImg,0,0,null);
        g2d.dispose();
        return background;
    }

    /**
     * 设置白色
     * @param width
     * @param height
     * @return
     */
    private static BufferedImage setWhite(int width, int height) {
        BufferedImage background = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                background.setRGB(j,i,0xFFFFFF);
            }
        }
        return background;
    }



    public static BufferedImage coverImage(BufferedImage baseBufferedImage, BufferedImage coverBufferedImage, int x, int y, int width, int height) throws Exception{

        // 创建Graphics2D对象，用在底图对象上绘图
            Graphics2D g2d = baseBufferedImage.createGraphics();
//        g2d.drawRect(x,y,width,height);
        // 绘制
        g2d.drawImage(coverBufferedImage, x, y, width, height, null);
        g2d.dispose();// 释放图形上下文使用的系统资源

        return baseBufferedImage;
    }

}
